ARouter 源码详解二

ARouter 源码分析

ARouter 的源码提供两个 SDK ,分别是面向不同的阶段,本身 API 这个SDK 是面向运行期的 ,而 compiler 这个 SDK 则是作用于编译期的,我们现在分析本身 API 这个 SDK

初始化

首先调用的是 ARouterinit 方法,代码如下:

public static void init(Application application) {
    if (!hasInit) {    // 确保只初始化一次
        hasInit = _ARouter.init(application);
        if (hasInit) {
            // 用于异步初始化 interceptor 
            _ARouter.afterInit();
        }
}

ARouter 使用了门面模式,真正起作用的是 _ARouter 类。很明显,这个方法分两步处理,分别是 _ARouter.init 方法_ARouter.afterInit 方法,先分析前一个。

final class _ARouter {
    // 默认日志类
    static ILogger logger = new DefaultLogger(Consts.TAG);
    // 线程池
    private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
    private static Handler mHandler;
    private static Context mContext;

    private static InterceptorService interceptorService;

    private _ARouter() {
    }

    protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;
        mHandler = new Handler(Looper.getMainLooper());

        return true;
    }
 }

_ARouter 是一个单例类,里面有一个默认的线程池对象,init 方法的核心代码是 LogisticsCenter.init(mContext, executor) ,接着看这个方法:

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;

    try {
           // 通过注册表插件来加载路由表
        loadRouterMap();
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            Set<String> routerMap;    // 保存生成类的类名集合
            // 如果是 Debug 模式或新安装的版本,从 apt 生成的包中加载类
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                // 通过指定的包名,加载由 compiler 自动生成的类
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                if (!routerMap.isEmpty()) {    // 加入 sp 缓存
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
                PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
            } else {
                // 从缓存中加载
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // This one of root elements, load root.
                    // 加载根分组,通过反射构建实例,将根分组信息保存在 Warehouse 的 groupsIndex 集合中
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // Load interceptorMeta
                    // 加载 Interceptors 相关信息
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceRouteMetaptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // Load providerIndex
                    // 加载 provider   相关信息
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }
    } catch (Exception e) {
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}

方法步骤总结如下:

  1. 先判断能否通过注册表插件来加载,可以则直接通过插件来处理,否则进行第二步。
  2. 如果是 Debug 模式或新安装的版本,则从 APT 生成的包中加载类并添加到缓存,否则直接从缓存中获取。 ClassUtils.getFileNameByPackageName 方法做的就是找到 appdex 文件 ,然后遍历出其中属于 com.alibaba.android.arouter.routes 包下的所有类名,打包成集合返回。可以想象遍历整个 dex 查找指定类名的工作量有多大,所以才有第一步的通过插件来加载。
  3. 遍历集合,反射实例化对象并调用方法,将注解生成的类的信息全部缓存到 Warehouse 类的相应变量中。之前讲注解处理器的时候有讲到,会通过 APT 生成一些配置信息类,现在这些信息全部缓存到 Warehouse 类中。

这里再看一下,比如分组的 Root 类,如下:

// 实现 IRouteRoot 接口
public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    // key 是分组名称,value 是对应的组的索引类 
    routes.put("service", ARouter$$Group$$service.class);
    routes.put("test", ARouter$$Group$$test.class);
  }
}

我们接着看一下 Warehouse 类:

class Warehouse {
    // Cache route and metas
    // Key 是根分组的名称,Value 为 对应的路由类的根类的 Class 类型
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    // Key 是 provider 的全类名,Value 为 对应的路由类型
    static Map<String, RouteMeta> providersIndex = new HashMap<>();
    static Map<Class, IProvider> providers = new HashMap<>();

    // Cache interceptor
    // Key 是 interceptor 的优先级,Value 为 对应的interceptor的Class类型
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

groupsIndex 对应着 Root 分组的所有配置信息,KEY 是分组名称,Value 为对应的分组索引类,索引类也由 APT 生成,管理着分组下所有的路由信息。 providersIndex 对应着 prvider 的配置信息,所有的服务类也都是 prvider 类,KEY 是服务接口的全类名,value 是一个 RouteMeta 类。 interceptorsIndex 对应着拦截器配置信息, Keyinterceptor 的优先级,Value 为对应的 interceptorClass 类型 。这跟我们讲 APT 生成的类是一样的,只不过是把所有的信息都加载到内存中罢了。

接下来,我们分析一下 _ARouter.afterInit 方法:

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

InterceptorService 是框架提供的核心接口,这个方法是用来获取到管理拦截器的服务类(InterceptorServiceImpl)的,这个服务类在初始化时会对所有拦截器进行初始化,该方法最终会实例化 InterceptorServiceImpl 类,并调用其 init 方法进行初始化:

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {

    @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class<? extends IInterceptor> interceptorClass = entry.getValue();
                        try {
                            // 反射构建拦截器实例
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            // 初始化
                            iInterceptor.init(context);
                            // 把实例添加到缓存中
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                        }
                    }

                    interceptorHasInit = true;

                    // 如果在进行路由时,拦截器还没有初始化完毕,会阻塞等待,初始完毕需要唤醒
                    synchronized (interceptorInitLock) {
                        interceptorInitLock.notifyAll();
                    }
                }
            }
        });
    }
}

该方法是在线程池中执行的,从 WarehouseinterceptorsIndex 中拿到所有拦截器的配置,然后遍历反射实例化对象并进行初始化,最后把实例添加到 Warehouseinterceptors 变量中,这个变量存储的就是所有的拦截器对象 :

//Warehouse.java
static List<IInterceptor> interceptors = new ArrayList<>();

小结:ARouter 的初始化主要是把 APT 生成的所有配置类的信息添加到内存中,并且对所有的拦截器进行了初始化,所以说拦截器的初始化时机跟 SDK 初始化时机是一样的。

路由操作

标准的路由操作如下:

ARouter.getInstance().build("/test/Test1Activity").navigation(activity);

那么我们先看一下 _ARouterbuild 方法:

protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        // 预留给用户实现路径动态变换功能
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        // 默认截取路径中的第一段作为分组名
        return build(path, extractGroup(path));
    }
}

首先会尝试实例化 PathReplaceService 对象,它继承 IProvider 接口,是预留给用户实现路径动态变换功能的,我们直接看最后面的代码,又调用了 build 的重载方法,第二个参数是截取的路由地址的第一段,即分组名,该方法如下:

protected Postcard build(String path, String group) {
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        // 这里会再次尝试动态变换路径
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        return new Postcard(path, group);
    }
}

该方法返回一个 Postcard 类,并把 pathgroup 传入构造方法中,那么我们看一下 Postcard 类:

public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;                
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // 用来传输数据的
    private int flags = -1;         // Flags of route
    private int timeout = 300;      // 路由超时时间,默认 300 秒
    private IProvider provider;     // 如果路由目标是一个 IProvider ,会被赋值.
    private boolean greenChannel;    // 是否走绿色通道
    private SerializationService serializationService;

    // Animation
    private Bundle optionsCompat;    // 界面转场动画
    private int enterAnim = -1;
    private int exitAnim = -1;

}

Postcard 类继承于 RouteMeta ,附加了一些跳转需要用到的信息。最后调用该 Postcard 对象的 navigation 方法,层层调用,最终还是调用的 _Arouternavigation 方法:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    // 预留给用户实现的预处理服务功能
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // Pretreatment failed, navigation canceled.
        return null;
    }
    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        if (debuggable()) {
            // Show friendly tips for user.
            // 在 debug 模式下,会弹 toast 友好提示
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +
                            " Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }
        // 回调路由找不到
        if (null != callback) {
            callback.onLost(postcard);
        } else {
            // 没有回调方法,则交给全局降级策略处理,由此可见,回调优先级大于降级策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                // 回调降级策略的 onLost 方法
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

    // 回调路由被找到
    if (null != callback) {
        callback.onFound(postcard);
    }

    // 如果不是绿色通道,即会触发拦截器,在后台线程处理,避免 ANR ,拦截器不宜处理耗时操作,不然跳转会超过导致失败
    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {

            @Override
            public void onContinue(Postcard postcard) {
                _navigation(context, postcard, requestCode, callback);
            }

            @Override
            public void onInterrupt(Throwable exception) {
                // 在子线程中回调
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        // 走绿色通道,没有拦截器,并有返回值
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
}

方法步骤总结如下:

  1. 尝试实例化 PretreatmentService 对象,它继承 IProvider 接口,是预留给用户实现的预处理服务功能的,如果有该接口实现类,会调用其 Init 方法进行初始化,并调用预处理方法 onPretreatment
  2. 调用 LogisticsCenter.completion(postcard) 方法尝试找到跳转的目标,找不到会走 catch 的逻辑,即第三步,否则走第四步。
  3. debug 模式下,会先弹 toast 友好提示,有监听回调,则回调其 onLost 方法,没有则会尝试寻找全局降级策略服务类,如果有该实现类,此时会回调降级策略服务类的 onLost 方法,否则直接返回 null
  4. 如果找到目标,有监听回调,则回调其 onFound 方法,表示路由已经找到。
  5. 判断是否走绿色通道,是则继续调用 _navigation 方法,否则触发拦截器。

我们先看一下 LogisticsCenter.completion(postcard) 是如何寻找目标路由的,这个方法代码量比较多,我们分两步来看:

public synchronized static void completion(Postcard postcard) {
        // 从缓存集合中查找,如果没有加载过,则为null
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
            // 找到该分组的根类
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
            if (null == groupMeta) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                try {
                    // 反射构建实例并添加到内存缓存中
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    // 由于已经加载实例到内存中了,则从根分组集合中移除该分组
                    Warehouse.groupsIndex.remove(postcard.getGroup());
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
                // 递归再次尝试加载
                completion(postcard);   // Reload
            }
        }else{
            ....省略后面代码
        }
}

方法步骤总结如下:

  1. 先从缓存集合中查找 RouteMeta 对象,没有则进入 if 里面的逻辑。
  2. 找到对应分组的索引类,索引类由 APT 生成的,保存着该分组的所有路由信息。
  3. 如果索引类不存在,则抛异常。
  4. 反射实例化该索引类,然后把所有配置信息加载到 Warehouseroutes 集合中,该集合的 KEY 是路由地址,VALUERouteMeta 对象。最后,由于已经加载实例到内存中了,则从根分组集合中移除该分组,避免重复加载。
  5. 再次尝试加载,此时会走 else 后面的逻辑。

我们接着分析 else 后面的逻辑,代码如下:

public synchronized static void completion(Postcard postcard) {
           ...
        // 从缓存集合中查找,如果没有加载过,则为null
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {   
            ...
        } else {
            // 赋值操作
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            // 如果是 Uri 跳转
            if (null != rawUri) {   // Try to set params into bundle.
                //分割路径中的参数
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                //获取Autowired注解的属性
                Map<String, Integer> paramsType = routeMeta.getParamsType();
                if (MapUtils.isNotEmpty(paramsType)) {
                    for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                        // 将对应的属性和值放入到 Postcard 对象的 Bundle 中
                        setValue(postcard,
                                params.getValue(),
                                params.getKey(),
                                resultMap.get(params.getKey()));
                    }
                    // 将需要自动注入的字段名称传入 Bundle
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }
                // 保存源 uri 路径到 Bundle 中
                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }

            switch (routeMeta.getType()) {
                // 如果是 PROVIDER 类型
                case PROVIDER:  
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            // 构建实例
                            provider = providerMeta.getConstructor().newInstance();
                            // 初始比
                            provider.init(mContext);
                            // 并添加到内存缓存中
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    // 保存 PROVIDER 实例到 Postcard 中
                    postcard.setProvider(instance);
                     // Provider不经过拦截器处理 
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                     // Fragment 不经过拦截器处理
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

方法步骤总结如下:

  1. 把找到的路由目标对应的 routeMeta 类的相关参数赋值到传入的 postcard 对象,此时 postcard 就具备了路由的相关信息了。
  2. 如果是通过 Uri 的方式跳转的,则分割路径中的参数并根据 Autowired 注解的属性,将对应的属性和值放入到 Postcard 对象的 Bundle 对象,这也就解释了为什么通过 URI 的方式跳转的时候,需要在属性上面加上 Autowired 注解,才能在对应的 Bundle 上获取到数据。
  3. 如果是 PROVIDER 类型,则反射实例化对象并调用其初始化方法 ,然后添加到内存缓存中,接着保存 PROVIDER 实例到 Postcard 中,最后设置该 Postcard 走绿色通道,因为 PROVIDER 类型不需要拦截器处理。这也就解释了服务是在用到的时候才会进行初始化的。
  4. 如果是 FRAGMENT 类型,则设置该 Postcard 走绿色通道,因为 FRAGMENT 类型也不需要拦截器处理。

我们先忽略拦截器的处理,接着看 _navigation 方法,代码如下:

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    switch (postcard.getType()) {
        case ACTIVITY:
            // ACTIVITY 类型,构建 Intent ,并把 postcard 的 Bundle 赋值给 Intent
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());

            // Set flags.
            int flags = postcard.getFlags();
            if (-1 != flags) {
                intent.setFlags(flags);
            } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                // 没有传入 Activity ,会添加这个 flag ,最好调用的时候传入Activity
                // 避免每开启一个Activity就会新开一个task,造成栈管理混乱
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            // Set Actions
            String action = postcard.getAction();
            if (!TextUtils.isEmpty(action)) {
                intent.setAction(action);
            }

            // Navigation in main looper.
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    // 如果不在主线程,则切换到主线程执行
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
        case PROVIDER:
            // 直接返回
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            Class fragmentMeta = postcard.getDestination();
            try {
                // 反射构建 Fragment 实例,并调用 setArguments 把 Bundle 传入
                Object instance = fragmentMeta.getConstructor().newInstance();
                if (instance instanceof Fragment) {
                    ((Fragment) instance).setArguments(postcard.getExtras());
                } else if (instance instanceof android.support.v4.app.Fragment) {
                    ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                }

                return instance;
            } catch (Exception ex) {
                logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
            }
        case METHOD:
        case SERVICE:
        default:
            return null;
    }
    // Activity 类型是返回 Null 的,拿不到实例
    return null;
}

方法步骤总结如下:

  1. ACTIVITY 类型,构建 Intent 并把 postcardBundle 赋值给 Intent,然后设置 Intentflagsactions ,如果不在主线程,会先切换到主线程进行界面跳转。(因为拦截器运行在子线程,里面也会回调这个方法,所以需要线程切换)。
  2. PROVIDER 类型,直接返回之前设置到 postcardprovider 对象即可。
  3. FRAGMENT 类型,反射实例化 Fragment 对象并调用其 setArguments 方法把 Bundle 传入,然后返回该实例。

注意:如果路由的时候没有传入 Activity 对象,但是跳转类型是 Activity 类型,则会调用 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 代码,这会导致每启动一个 Activity 就会新开一个 task ,所以建议普通界面跳转都传入 Activity ,避免栈的混乱。

拦截过程分析

在分析拦截器的过程之前,我们先了解一下 CountDownLatch 类,简单来说 CountDownLatch 可以阻塞一个线程,直到内部计数器为 0 时才继续执行阻塞的线程,计数器的初始值通过构造传入,通过调用 countDown() 方法减少一个计数。 CancelableCountDownLatch 类继承自 CountDownLatch ,并增加了 cancel 方法,用于直接将计数归 0 ,放开阻塞:

public void cancel() {
    // 循环调用 countDown 方法,直到 count 为 0,放开阻塞
    while (getCount() > 0) {
        countDown();
    }
}

现在正式来分析拦截过程,拦截功能是通过 ARouter 提供的 interceptorService 实现的,我们在初始化分析时已经提到过,接下来看看具体是如何拦截的:

interceptorService.doInterceptions(postcard, new InterceptorCallback() {

    @Override
    public void onContinue(Postcard postcard) {
        // 继续路由跳转
        _navigation(context, postcard, requestCode, callback);
    }

    @Override
    public void onInterrupt(Throwable exception) {
        // 在子线程中回调
        if (null != callback) {
            callback.onInterrupt(postcard);
        }
    }
});

该方法将会在子线程中运行,所以需要回调的方式来处理结果,onContinue 方法会接着走我们已经分析的路由跳转逻辑,而 onInterrupt 方法会回调到路由跳转时传入的监听回调的 onInterrupt 方法。我们接着分析 doInterceptions 方法:

// InterceptorServiceImpl.java
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    // 如果有拦截器
    if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
        // 检查拦截器的初始化状态
        checkInterceptorsInitStatus();
        if (!interceptorHasInit) {
            callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
            return;
        }
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                // 构建 CancelableCountDownLatch ,初始计数为拦截器的数量
                CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                try {
                    _excute(0, interceptorCounter, postcard);
                    // 阻塞线程直到超时,或者计数归0
                    // 超时时间默认为 300 秒
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                        // 拦截超时
                        callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                    } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                        // 被拦截
                        callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                    } else {
                        // 放行
                        callback.onContinue(postcard);
                    }
                } catch (Exception e) {
                    callback.onInterrupt(e);
                }
            }
        });
    } else {
        callback.onContinue(postcard);
    }
}

private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
    //如果不是最后一个拦截器
    if (index < Warehouse.interceptors.size()) {
        // 索引从0开始,即从第一个拦截器开始获取
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);
        iInterceptor.process(postcard, new InterceptorCallback() {
            @Override
            public void onContinue(Postcard postcard) {
                // Last interceptor excute over with no exception.
                // 计数减1,放行到下一个拦截器处理
                counter.countDown();
                _excute(index + 1, counter, postcard);  
            }

            @Override
            public void onInterrupt(Throwable exception) {
                // 拦截,将 Exception 传入到 postcard 的 tag 字段,
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                // 计数归0
                counter.cancel();
            }
        });
    }
}

该方法首先判断是否存在拦截器,没有则直接回调 onContinue 方法,有则会检查拦截器的初始化状态:

private static void checkInterceptorsInitStatus() {
    synchronized (interceptorInitLock) {
        while (!interceptorHasInit) {
            try {
                // 等待10秒钟
                interceptorInitLock.wait(10 * 1000);
            } catch (InterruptedException e) {
                throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
            }
        }
    }
}

方法很简单,判断 interceptorHasInit 是否为 ture ,否则等待 10 秒让拦截器进行初始化,初始化成功后会将 interceptorHasInit 设置为 true ,否则会抛异常。因此,拦截器不宜太多,而且不要在拦截器里面做耗时操作,不然跳转响应会很慢。

接着会通过线程池执行一个 Runnable 对象,然后会创建一个与拦截器数量相同的 CancelableCountDownLatch 初始计数值,每放行一个拦截器就 countDown ,并交给后一个拦截器处理,如果拦截了则直接归 0 计数,并将拦截的 Throwable 存入 postcardtag 字段,interceptorCounter.await() 会阻塞直到计数归 0 或者阻塞超时(默认是300秒),最后通过 interceptorCounter.getCount() 判断是否是超时,还是拦截或者放行。

注意:拦截的过程都是在子线程中处理,包括 Interceptor 的 process 也是在子线程调用的,因此,如果想要在拦截过程中展示 dialog 等操作都需要切换到主线程。

数据注入

到此,跳转逻辑已全部分析完毕,我们接着分析一下数据注入的逻辑。我们知道自动注入功能需要实现如下代码:

ARouter.getInstance().inject(this);

最终会调用 _ARouterinject 方法:

static void inject(Object thiz) {
    // 获取框架内的注入服务实例
    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
    if (null != autowiredService) {
        // 调用 autiowire 方法
        autowiredService.autowire(thiz);
    }
}

AutowiredService 跟之前的降级服务和拦截器服务一样,都是框架提供的核心接口,我们看一下它的实现类:

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

    @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                // 如果没有缓存
                if (null == autowiredHelper) {  // No cache.
                    //则构建一个注入辅助类,比如要注入的Activity 是 Test3Activity ,则这里获取的就是
                    //    Test3Activity$$ARouter$$Autowired,这是通过 apt 自动生成的
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }
}

方法很清晰,用到了 LruCache 来做缓存处理,然后获取之前通过 APT 生成的注入辅助类,并调用其 inject 方法,这一部分我们之前已经分析过了,这里再看一下辅助类的代码:

public class Test3Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    Test3Activity substitute = (Test3Activity)target;
    substitute.name = substitute.getIntent().getStringExtra("teacherName");
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
    substitute.testPac = substitute.getIntent().getParcelableExtra("testPac");
    if (null != serializationService) {
      substitute.testObj = serializationService.parseObject(substitute.getIntent().getStringExtra("testObj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'testObj' in class 'Test3Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
  }
}

注意:这里赋值的操作是直接调用“目标类对象.属性”的方式赋值,因此, private 修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常,被 AutowiredServiceImplautowire 方法中的 try-catch 捕获,存入不需要注入的集合中,最终导致同一个类中的其他非 private 属性也无法注入。